// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: develop dispatcher
// feature: Unified Transport Abstraction Layer Unified Transport Abstraction Layer
// feature: discovery support client and server
// feature: interfaceWhitelist supports configuring network segments
// feature: support multiple sending based on topic
// ------------------------------------------------------------------

#include <utility>
#include <cstring>
#include <algorithm>

#include <elog/Log.hpp>
#include <ertps/utils/LocatorAlias.h>
#include <deps/common/IPLocator.h>
#include <deps/common/IPFinder.h>

#include <transport/TransportInterface.h>
#include <transport/ReceiverResource.h>
#include <transport/SenderResource.h>
#include "UDPv4Transport.h"
#include "UDPSenderResource.hpp"

using namespace std;
using namespace asio;

namespace vbs {
namespace transport {

using IPFinder = vbsutil::xmlparser::IPFinder;
using IPLocator = vbsutil::xmlparser::IPLocator;

static void get_ipv4s(std::vector<IPFinder::info_IP>& locNames, bool return_loopback = false) {
    (void)IPFinder::getIPs(&locNames, return_loopback);
    auto new_end = remove_if(locNames.begin(), locNames.end(), [](IPFinder::info_IP ip) {
        return ((ip.type != IPFinder::IP4) && (ip.type != IPFinder::IP4_LOCAL));
    });
    (void)locNames.erase(new_end, locNames.end());
    (void)std::for_each(locNames.begin(), locNames.end(),
                        [](IPFinder::info_IP& loc) { loc.locator.kind = LOCATOR_KIND_UDPv4; });
}

static void get_ipv4s_unique_interfaces(std::vector<IPFinder::info_IP>& locNames, bool return_loopback = false) {
    get_ipv4s(locNames, return_loopback);
    std::sort(locNames.begin(), locNames.end(),
              [](const IPFinder::info_IP& a, const IPFinder::info_IP& b) -> bool { return a.dev < b.dev; });
    auto new_end = std::unique(
        locNames.begin(), locNames.end(), [](const IPFinder::info_IP& a, const IPFinder::info_IP& b) -> bool {
            return (a.type != IPFinder::IP4_LOCAL) && (b.type != IPFinder::IP4_LOCAL) && (a.dev == b.dev);
        });
    (void)locNames.erase(new_end, locNames.end());
}

static asio::ip::address_v4::bytes_type Locator_to_native(const Locator& locator) {
    if (IPLocator::hasWan(locator)) {
        return {{IPLocator::getWan(locator)[0], IPLocator::getWan(locator)[1], IPLocator::getWan(locator)[2],
                 IPLocator::getWan(locator)[3]}};
    } else {
        return {{IPLocator::getIPv4(locator)[0], IPLocator::getIPv4(locator)[1], IPLocator::getIPv4(locator)[2],
                 IPLocator::getIPv4(locator)[3]}};
    }
}

UDPv4Transport::UDPv4Transport(const UDPv4TransportDescriptor& descriptor)
    : UDPTransportInterface(LOCATOR_KIND_UDPv4), configuration_(descriptor) {
    mSendBufferSize = descriptor.sendBufferSize;
    mReceiveBufferSize = descriptor.receiveBufferSize;
#ifdef EDDS_XDP_TRANSPORT_COMPILE
    if (descriptor.socket_type_ == "xdp") {
        set_socket_type(SOCKET_TYPE_XDP);
    } else {
        set_socket_type(SOCKET_TYPE_UDP);
    }
#endif
    if (!descriptor.interfaceWhiteList.empty()) {
        int i = 0;
        while (i < 20) {
            std::vector<IPFinder::info_IP> local_interfaces;
            get_ipv4s(local_interfaces, true);
            for (auto& waddr : descriptor.interfaceWhiteList) {
                auto it = std::find_if(local_interfaces.begin(), local_interfaces.end(),
                                       [&waddr](const IPFinder::info_IP& ip) {
                                           return (waddr.addr == ip.name || waddr.addr == ip.dev ||
                                                   (IPLocator::isNetworkAddress(waddr.addr, ip.mask_len) &&
                                                    IPLocator::isSameNetworkSegment(waddr.addr, ip.name, ip.mask_len)));
                                       });

                if (it != local_interfaces.end()) {
                    auto white =
                        std::find_if(interface_whitelist_.begin(), interface_whitelist_.end(),
                                     [&it](const WhiteListAddr& white_addr) { return (white_addr.addr == it->name); });
                    if (white == interface_whitelist_.end()) {
                        WhiteListAddr newAddr = waddr;
                        newAddr.addr = it->name;
                        interface_whitelist_.emplace_back(newAddr);
                        logDebug(TRANSPORT, "Add address: " << newAddr.addr << " of interface(" << it->dev
                                                            << ") To interfaceWhiteList.");
                    }
                } else {
                    logDebug(TRANSPORT, "Address: " << waddr.addr << " were filtered out.");
                }
            }
            if (!interface_whitelist_.empty()) {
                break;
            } else {
                std::this_thread::sleep_for(std::chrono::milliseconds(250));
                i++;
            }
        }

        if (interface_whitelist_.empty()) {
            elogError(TRANSPORT, RetCode_t::RETCODE_BAD_PARAMETER, "All whitelist interfaces were filtered out");
            throw std::runtime_error("XML whitelist config error");
        } else if (i > 0) {
            logWarning(TRANSPORT, "Add address to interfaceWhiteList success, try times(250ms):" << i);
        }
    }
}

UDPv4Transport::UDPv4Transport() : UDPTransportInterface(LOCATOR_KIND_UDPv4) {}

UDPv4Transport::~UDPv4Transport() {
    clean();
}

UDPv4TransportDescriptor::UDPv4TransportDescriptor() : UDPTransportDescriptor() {}

TransportInterface* UDPv4TransportDescriptor::create_transport() const {
    try {
        return new UDPv4Transport(*this);
    } catch (const asio::invalid_service_owner& e) {
        elogError(TRANSPORT, RetCode_t::RETCODE_BAD_PARAMETER, "new UDPv4Transport(*this) catch err: " << e.what());
        return nullptr;
    } catch (const asio::system_error& e) {
        elogError(TRANSPORT, RetCode_t::RETCODE_ERROR, "new UDPv4Transport(*this) catch system_error: " << e.what());
        return nullptr;
    } catch (const asio::service_already_exists& e) {
        elogError(TRANSPORT, RetCode_t::RETCODE_PRECONDITION_NOT_MET,
                  "new UDPv4Transport(*this) catch asio::service_already_exists: " << e.what());
        return nullptr;
    } catch (const std::exception& e) {
        elogError(TRANSPORT, RetCode_t::RETCODE_ERROR, "new UDPv4Transport(*this) catch other error: " << e.what());
        return nullptr;
    }
}

bool UDPv4TransportDescriptor::operator==(const UDPv4TransportDescriptor& t) const {
    return (UDPTransportDescriptor::operator==(t));
}

bool UDPv4Transport::getDefaultMetatrafficMulticastLocators(LocatorList& locators,
                                                            uint32_t metatraffic_multicast_port) const {
    Locator locator;
    locator.kind = LOCATOR_KIND_UDPv4;
    locator.port = static_cast<uint16_t>(metatraffic_multicast_port);
    (void)IPLocator::setIPv4(locator, DEFAULT_METATRAFFIC_MULTICAST_ADDRESS);
    locators.push_back(locator);
    return true;
}

bool UDPv4Transport::getDefaultMetatrafficUnicastLocators(LocatorList& locators,
                                                          uint32_t metatraffic_unicast_port) const {
    Locator locator;
    locator.kind = LOCATOR_KIND_UDPv4;
    locator.port = static_cast<uint16_t>(metatraffic_unicast_port);
    locator.set_Invalid_Address();
    locators.push_back(locator);

    return true;
}

bool UDPv4Transport::getDefaultUnicastLocators(LocatorList& locators, uint32_t unicast_port) const {
    Locator locator;
    locator.kind = LOCATOR_KIND_UDPv4;
    locator.set_Invalid_Address();
    (void)fillUnicastLocator(locator, unicast_port);
    locators.push_back(locator);

    return true;
}

void UDPv4Transport::AddDefaultOutputLocator(LocatorList& defaultList) {
    Locator locator;
    IPLocator::createLocator(LOCATOR_KIND_UDPv4, DEFAULT_METATRAFFIC_MULTICAST_ADDRESS,
                             configuration_.m_output_udp_socket, locator);
    defaultList.push_back(locator);
}

bool UDPv4Transport::compare_locator_ip(const Locator& lh, const Locator& rh) const {
    return IPLocator::compareAddress(lh, rh);
}

bool UDPv4Transport::compare_locator_ip_and_port(const Locator& lh, const Locator& rh) const {
    return IPLocator::compareAddressAndPhysicalPort(lh, rh);
}

void UDPv4Transport::endpoint_to_locator(ip::udp::endpoint& endpoint, Locator& locator) {
    locator.kind = kind();
    (void)IPLocator::setPhysicalPort(locator, endpoint.port());
    auto ipBytes = endpoint.address().to_v4().to_bytes();
    (void)IPLocator::setIPv4(locator, ipBytes.data());
}

void UDPv4Transport::fill_local_ip(Locator& loc) const {
    loc.kind = kind();
    (void)IPLocator::setIPv4(loc, "127.0.0.1");
}

asio::ip::udp::endpoint UDPv4Transport::GenerateAnyAddressEndpoint(uint16_t port) {
    return ip::udp::endpoint(ip::address_v4::any(), port);
}

ip::udp::endpoint UDPv4Transport::generate_endpoint(const std::string& sIp, uint16_t port) {
    return asio::ip::udp::endpoint(ip::address_v4::from_string(sIp), port);
}

ip::udp::endpoint UDPv4Transport::generate_endpoint(uint16_t port) {
    return asio::ip::udp::endpoint(asio::ip::udp::v4(), port);
}

ip::udp::endpoint UDPv4Transport::generate_local_endpoint(const Locator& loc, uint16_t port) {
    return ip::udp::endpoint(asio::ip::address_v4(Locator_to_native(loc)), port);
}

asio::ip::udp UDPv4Transport::generate_protocol() const {
    return ip::udp::v4();
}

void UDPv4Transport::get_ips(std::vector<IPFinder::info_IP>& locNames, bool return_loopback) {
    get_ipv4s(locNames, return_loopback);
}

const std::string& UDPv4Transport::localhost_name() {
    static const std::string ip4_localhost = "127.0.0.1";
    return ip4_localhost;
}

vbsUDPSocket UDPv4Transport::OpenAndBindInputSocket(const std::string& sIp, uint16_t port, bool is_multicast) {
    vbsUDPSocket socket = createUDPSocket(io_service_);
    getSocketPtr(socket)->open(generate_protocol());
    if (mReceiveBufferSize != 0) {
        getSocketPtr(socket)->set_option(socket_base::receive_buffer_size(mReceiveBufferSize));
    }

    if (is_multicast) {
        getSocketPtr(socket)->set_option(ip::udp::socket::reuse_address(true));
#if defined(__QNX__)
        getSocketPtr(socket)->set_option(
            asio::detail::socket_option::boolean<ASIO_OS_DEF(SOL_SOCKET), SO_REUSEPORT>(true));
#endif  // if defined(__QNX__)
    }

    getSocketPtr(socket)->bind(generate_endpoint(sIp, port));
    return socket;
}

bool UDPv4Transport::OpenInputChannel(const Locator& locator, std::shared_ptr<ReceiverResource> receiver,
                                      uint32_t maxMsgSize) {
    std::unique_lock<std::recursive_mutex> scopedLock(mInputMapMutex);
    if (!is_locator_allowed(locator)) {
        return false;
    }
    logDebug(RTPS_TRANSPORT, "OpenInputChannel ");

    bool success = false;

    if (!IsInputChannelOpen(locator)) {
        success = OpenAndBindInputSockets(locator, receiver, IPLocator::isMulticast(locator), maxMsgSize);
    }

    if (IPLocator::isMulticast(locator) && IsInputChannelOpen(locator)) {
        std::string locatorAddressStr = IPLocator::toIPv4string(locator);
        ip::address_v4 locatorAddress = ip::address_v4::from_string(locatorAddressStr);

#ifndef _WIN32
        if (!is_interface_whitelist_empty()) {
            // Either wildcard address or the multicast address needs to be bound on non-windows
            // systems
            bool found = false;

            // First check if the multicast address is already bound
            auto& channelResources = mInputSockets.at(IPLocator::getPhysicalPort(locator));
            for (UDPChannelResource* channelResource : channelResources) {
                if (channelResource->interface() == locatorAddressStr) {
                    found = true;
                    break;
                }
            }

            // Create a new resource if no one is found
            if (!found) {
                try {
                    // Bind to multicast address
                    UDPChannelResource* p_channel_resource;
                    p_channel_resource =
                        CreateInputChannelResource(locatorAddressStr, locator, true, maxMsgSize, std::move(receiver));
                    mInputSockets[IPLocator::getPhysicalPort(locator)].push_back(p_channel_resource);

                    // Join group on all whitelisted interfaces
                    for (auto& ip : interface_whitelist_) {
                        p_channel_resource->socket()->set_option(
                            ip::multicast::join_group(locatorAddress, ip::address_v4::from_string(ip.addr)));
                    }
                } catch (asio::system_error const& e) {
                    (void)e;
                    logWarning(RTPS_MSG_OUT, "UDPTransport Error binding " << locatorAddressStr << " at port: ("
                                                                           << IPLocator::getPhysicalPort(locator) << ")"
                                                                           << " with msg: " << e.what());
                }
            }
        } else
#endif  // ifndef _WIN32
        {
            // The multicast group will be joined silently, because we do not
            // want to return another resource.
            auto& channelResources = mInputSockets.at(IPLocator::getPhysicalPort(locator));
            for (UDPChannelResource* channelResource : channelResources) {
                if (channelResource->interface() == s_IPv4AddressAny) {
                    std::vector<IPFinder::info_IP> locNames;
                    get_ipv4s_unique_interfaces(locNames, true);
                    for (const auto& infoIP : locNames) {
                        auto ip = asio::ip::address_v4::from_string(infoIP.name);
                        try {
                            channelResource->socket()->set_option(ip::multicast::join_group(locatorAddress, ip));
                        } catch (std::system_error& ex) {
                            (void)ex;
                            logWarning(RTPS_MSG_OUT, "Error joining multicast group on " << ip << ": " << ex.what());
                        }
                    }
                } else {
                    auto ip = asio::ip::address_v4::from_string(channelResource->interface());
                    try {
                        channelResource->socket()->set_option(ip::multicast::join_group(locatorAddress, ip));
                    } catch (std::system_error& ex) {
                        (void)ex;
                        logWarning(RTPS_MSG_OUT, "Error joining multicast group on " << ip << ": " << ex.what());
                    }
                }
            }
        }
    }

    return success;
}

std::vector<std::string> UDPv4Transport::get_binding_interfaces_list() {
    std::vector<std::string> vOutputInterfaces;
    if (is_interface_whitelist_empty()) {
        vOutputInterfaces.push_back(s_IPv4AddressAny);
    } else {
        for (auto& ip : interface_whitelist_) {
            vOutputInterfaces.push_back(ip.addr);
        }
    }

    return vOutputInterfaces;
}

bool UDPv4Transport::is_interface_allowed(const std::string& interface) const {
    if (interface_whitelist_.empty()) {
        return true;
    }

    // if (ip == ip::address_v4::any()) {
    if (interface == ip::address_v4::any().to_string()) {
        return true;
    }

    auto it = std::find_if(interface_whitelist_.begin(), interface_whitelist_.end(),
                           [&interface](const WhiteListAddr& waddr) { return waddr.addr == interface; });

    if (it != interface_whitelist_.end()) {
        return true;
    } else {
        return false;
    }
}

bool UDPv4Transport::is_interface_allowed(const std::string& interface, uint32_t& order) const {
    if (interface_whitelist_.empty()) {
        return true;
    }

    // if (ip == ip::address_v4::any()) {
    if (interface == ip::address_v4::any().to_string()) {
        return true;
    }

    auto it = std::find_if(interface_whitelist_.begin(), interface_whitelist_.end(),
                           [&interface](const WhiteListAddr& waddr) { return waddr.addr == interface; });

    if (it != interface_whitelist_.end()) {
        order = (*it).order;
        return true;
    } else {
        return false;
    }
}

void UDPv4Transport::create_sender_resource_with_whitelist(SendResourceList& sender_resource_list, uint16_t port) {
    IPFinder::info_IP infoIP;
    std::vector<IPFinder::info_IP> locNames;
    logDebug(RTPS_MSG_OUT, "OpenOutputChannel with witelist");

    get_unknown_network_interfaces(sender_resource_list, locNames, true);
    for (auto& ip : interface_whitelist_) {
        // if (ip == ip::address_v4::any()) {
        try {
            if (ip.addr != ip::address_v4::any().to_string()) {
                auto infoIP = std::find_if(locNames.begin(), locNames.end(),
                                           [&ip](const IPFinder::info_IP& info) { return ip.addr == info.name; });
                if (infoIP != locNames.end()) {
                    uint16_t real_port = 0;
                    bool is_xdp_created = false;
                    vbsUDPSocket unicastSocket = createUDPSocket(io_service_);
#ifdef EDDS_XDP_TRANSPORT_COMPILE
                    LixdpSocketUDP* lixdp_socket = nullptr;

                    if (socket_type_ == SocketType::SOCKET_TYPE_XDP) {
                        lixdp_socket = new LixdpSocketUDP(infoIP->name, real_port);
                        int ret = lixdp_socket->lixdp_init(lixdp::LIXDP_MODE_SEND_ONLY);
                        if (ret != 0) {
                            logError(RTPS_MSG_OUT,
                                     "lixdp_init failed, ip: " << infoIP->name << ", port: 0, ret: " << ret);
                            delete lixdp_socket;
                            lixdp_socket = nullptr;
                            is_xdp_created = false;
                        } else {
                            is_xdp_created = true;
                            real_port = lixdp_socket->get_port();
                            lixdp_socket->set_local_port(real_port);
                        }
                    }
#endif
                    if (!is_xdp_created) {
                        unicastSocket =
                            OpenAndBindUnicastOutputSocket(generate_endpoint(infoIP->name, port), real_port);
                        SetSocketOutboundInterface(unicastSocket, infoIP->name);
                        // set DF is FALSE
#if defined(_WIN32)
                    BOOL df_flag = FALSE;
                    if (setsockopt(getSocketPtr(unicastSocket)->native_handle(), IPPROTO_IP, IP_DONTFRAGMENT, (const char*)&df_flag, sizeof(df_flag)) == SOCKET_ERROR) {
                        logError(RTPS_MSG_OUT, "setsockopt failed to set IP_DONTFRAGMENT with error: " << WSAGetLastError());
                    }
#else
                    int pmtu = IP_PMTUDISC_DONT;  // 设置为允许分片
                    if (setsockopt(getSocketPtr(unicastSocket)->native_handle(), IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)) != 0) {
                        logError(RTPS_MSG_OUT, "setsockopt failed to set IP_MTU_DISCOVER with error: " << strerror(errno));
                    }
#endif
                        if (first_time_open_output_channel_) {
                            getSocketPtr(unicastSocket)->set_option(ip::multicast::enable_loopback(true));
                            first_time_open_output_channel_ = false;
                        }
                    }

                    bool added = false;
                    for (auto it = sender_resource_list.begin(); it != sender_resource_list.end(); it++) {
                        if ((*it)->order() > static_cast<int32_t>(ip.order)) {
                            sender_resource_list.insert(
                                it, std::unique_ptr<vbs::transport::SenderResource>(
                                        new UDPSenderResource(*this, unicastSocket,
#ifdef EDDS_XDP_TRANSPORT_COMPILE
                                                              lixdp_socket,
#endif
                                                              infoIP->mask_len, false, true, ip.order)));
#ifdef EDDS_XDP_TRANSPORT_COMPILE
                            logInfo(RTPS_MSG_OUT, "UDPTransport(output) insert IP/DEV:"
                                                      << infoIP->name << ":" << real_port
                                                      << " mask:" << infoIP->mask_len << " order: " << ip.order
                                                      << " mode:" << (lixdp_socket != nullptr ? "XDP" : "UDP")
                                                      << " with whitelist.");
#else
                            logInfo(RTPS_MSG_OUT, "UDPTransport(output) insert IP/DEV:"
                                                      << infoIP->name << ":" << real_port
                                                      << " mask:" << infoIP->mask_len << " order: " << ip.order
                                                      << " with whitelist.");
#endif
                            added = true;
                            break;
                        }
                    }

                    if (!added) {
                        sender_resource_list.emplace_back(static_cast<vbs::transport::SenderResource*>(
                            new UDPSenderResource(*this, unicastSocket,
#ifdef EDDS_XDP_TRANSPORT_COMPILE
                                                  lixdp_socket,
#endif
                                                  infoIP->mask_len, false, true, ip.order)));
#ifdef EDDS_XDP_TRANSPORT_COMPILE
                        logInfo(RTPS_MSG_OUT, "UDPTransport(output) IP/DEV:"
                                                  << infoIP->name << ":" << real_port << " mask:" << infoIP->mask_len
                                                  << " order: " << ip.order << " mode:"
                                                  << (lixdp_socket != nullptr ? "XDP" : "UDP") << " with whitelist.");
#else
                        logInfo(RTPS_MSG_OUT, "UDPTransport(output) IP/DEV:"
                                                  << infoIP->name << ":" << real_port << " mask:" << infoIP->mask_len
                                                  << " order: " << ip.order << " with whitelist.");
#endif
                    }
                }
            }
        } catch (const std::exception& ex) {
            elogError(RTPS_MSG_OUT, RetCode_t::RETCODE_ERROR,
                      "UDPTransport create_sender_resource_with_whitelist: " << ex.what() << " port:" << port);
        }
    }
}

bool UDPv4Transport::is_interface_whitelist_empty() const {
    return interface_whitelist_.empty();
}

inline bool UDPv4Transport::is_locator_allowed(const Locator& locator) const {
    if (!IsLocatorSupported(locator)) {
        return false;
    }
    if (interface_whitelist_.empty() || IPLocator::isMulticast(locator)) {
        return true;
    }
    return is_interface_allowed(IPLocator::toIPv4string(locator));
}

LocatorList UDPv4Transport::NormalizeLocator(const Locator& locator) {
    LocatorList list;

    if (IPLocator::isAny(locator)) {
        std::vector<IPFinder::info_IP> locNames;
        get_ipv4s(locNames);
        for (const auto& infoIP : locNames) {
            // auto ip = asio::ip::address_v4::from_string(infoIP.name);
            if (is_interface_allowed(infoIP.name)) {
                Locator newloc(locator);
                (void)IPLocator::setIPv4(newloc, infoIP.locator);
                list.push_back(newloc);
            }
        }
        if (list.empty()) {
            Locator newloc(locator);
            (void)IPLocator::setIPv4(newloc, "127.0.0.1");
            list.push_back(newloc);
        }
    } else {
        list.push_back(locator);
    }

    return list;
}

bool UDPv4Transport::is_local_locator(const Locator& locator) const {
    assert(locator.kind == LOCATOR_KIND_UDPv4);

    if (IPLocator::isLocal(locator)) {
        return true;
    }

    for (const IPFinder::info_IP& localInterface : currentInterfaces) {
        if (IPLocator::compareAddress(locator, localInterface.locator)) {
            return true;
        }
    }

    return false;
}

void UDPv4Transport::set_receive_buffer_size(uint32_t size) {
    configuration_.receiveBufferSize = size;
}

void UDPv4Transport::set_send_buffer_size(uint32_t size) {
    configuration_.sendBufferSize = size;
}

void UDPv4Transport::SetSocketOutboundInterface(vbsUDPSocket& socket, const std::string& sIp) {
    getSocketPtr(socket)->set_option(ip::multicast::outbound_interface(asio::ip::address_v4::from_string(sIp)));
}

void UDPv4Transport::update_network_interfaces() {
    UDPTransportInterface::update_network_interfaces();
    for (auto& channelResources : mInputSockets) {
        for (UDPChannelResource* channelResource : channelResources.second) {
            if (channelResource->interface() == s_IPv4AddressAny) {
                std::vector<IPFinder::info_IP> locNames;
                get_ipv4s_unique_interfaces(locNames, true);
                for (const auto& infoIP : locNames) {
                    auto ip = asio::ip::address_v4::from_string(infoIP.name);
                    try {
                        channelResource->socket()->set_option(ip::multicast::join_group(
                            ip::address_v4::from_string(DEFAULT_METATRAFFIC_MULTICAST_ADDRESS), ip));
                    } catch (std::system_error& ex) {
                        (void)ex;
                        logWarning(RTPS_MSG_OUT, "Error joining multicast group on " << ip << ": " << ex.what());
                    }
                }
            } else {
                auto ip = asio::ip::address_v4::from_string(channelResource->interface());
                try {
                    channelResource->socket()->set_option(ip::multicast::join_group(
                        ip::address_v4::from_string(DEFAULT_METATRAFFIC_MULTICAST_ADDRESS), ip));
                } catch (std::system_error& ex) {
                    (void)ex;
                    logWarning(RTPS_MSG_OUT, "Error joining multicast group on " << ip << ": " << ex.what());
                }
            }
        }
    }
}

}  // namespace transport
}  // namespace vbs
